package view_server

import (
	"bigdevops/src/common"
	"bigdevops/src/config"
	"bigdevops/src/models"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	"github.com/prometheus/alertmanager/pkg/labels"
	"github.com/prometheus/alertmanager/types"
	"github.com/prometheus/common/model"
	"go.uber.org/zap"
	"strconv"
	"time"
)

type EventSilenceRequest struct {
	SilenceTime string `json:"silence_time"`
	ByName      bool   `json:"by_name"`
}

func updateMonitorAlertEvent(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	var reqObj models.MonitorAlertEvent
	err := c.ShouldBindJSON(&reqObj)
	if err != nil {
		sc.Logger.Error("解析新增告警事件请求失败", zap.Any("告警事件", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 在这里校验字段，是否必填，范围是否正确
	err = validate.Struct(reqObj)
	if err != nil {

		// 这里为什么要判断错误是否是 ValidationErrors
		if errors, ok := err.(validator.ValidationErrors); ok {
			common.ReqBadFailWithWithDetailed(

				gin.H{
					"翻译前": err.Error(),
					"翻译后": errors.Translate(trans),
				},
				"请求出错",
				c,
			)
			return
		}
		common.ReqBadFailWithMessage(err.Error(), c)
		return

	}

	_, err = models.GetMonitorAlertEventById(int(reqObj.ID))
	if err != nil {
		sc.Logger.Error("根据id找告警事件错误", zap.Any("告警事件", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	err = reqObj.UpdateOne()

	if err != nil {
		sc.Logger.Error("更新告警事件本身错误", zap.Any("告警事件", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	common.OkWithMessage("更新成功", c)
}

func getMonitorAlertEventList(c *gin.Context) {

	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	currentPage, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "100"))

	offset := 0
	limit := 0
	limit = pageSize
	if currentPage > 1 {
		offset = (currentPage - 1) * limit
	}
	// 这里只能新增 不能去掉，因为是 多种资源共用的query
	//searchUserID := c.DefaultQuery("UserID", "")
	//searchUserIDInt, _ := strconv.Atoi(searchUserID)
	searchName := c.DefaultQuery("name", "")

	// 	数据库中拿到所有的menu列表

	objs, err := models.GetMonitorAlertEventAll()
	if err != nil {
		sc.Logger.Error("去数据库中拿所有的告警事件错误",
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("去数据库中拿所有的告警事件错误:%v", err.Error()), c)
		return
	}

	allIds := []int{}
	//// 遍历 role 准备menuIds 列表
	for _, obj := range objs {
		obj := obj

		// 在这里处理查询
		if searchName != "" && obj.AlertName != searchName {
			continue
		}

		allIds = append(allIds, int(obj.ID))

		//role.MenuIds = menuIds
	}

	if len(allIds) == 0 {
		common.OkWithDetailed(allIds, "ok", c)
		return
	}
	objs, err = models.GetMonitorAlertEventByIdsWithLimitOffset(allIds, limit, offset)
	if err != nil {
		sc.Logger.Error("limit-offset去数据库中拿所有的任务错误",
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("limit-offset去数据库中拿所有的告警事件错误:%v", err.Error()), c)
		return
	}
	for _, obj := range objs {
		obj.FillFrontAllData()

	}

	resp := &ResponseResourceCommon{
		//Total: models.GetMonitorAlertEventCount(), 因为是带了查询条件的 所以 count 不能是all 而是 过滤后的total

		Total: len(allIds),
		Items: objs,
	}

	common.OkWithDetailed(resp, "ok", c)
}

func getMonitorAlertEventOne(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	// 校验一下 menu字段
	id := c.Param("id")

	// 先 去db中根据id找到这个user
	intVar, _ := strconv.Atoi(id)
	dbObj, err := models.GetMonitorAlertEventById(intVar)
	if err != nil {
		sc.Logger.Error("根据id找任务实例错误", zap.Any("任务实例", id), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}
	//isRelatedWithMe := false

	//userName := c.MustGet(common.GIN_CTX_JWT_USER_NAME).(string)
	//dbUser, err := models.GetUserByUserName(userName)
	//if err != nil {
	//	sc.Logger.Error("通过token解析到的userName去数据库中找User失败",
	//		zap.Error(err),
	//	)
	//	common.ReqBadFailWithMessage(fmt.Sprintf("通过token解析到的userName去数据库中找User失败:%v", err.Error()), c)
	//	return
	//}

	dbObj.FillFrontAllData()

	common.OkWithData(dbObj, c)
}

func deleteMonitorAlertEvent(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	// 校验一下 menu字段
	id := c.Param("id")
	sc.Logger.Info("删除告警事件", zap.Any("id", id))

	// 先 去db中根据id找到这个对象
	intVar, _ := strconv.Atoi(id)
	dbObj, err := models.GetMonitorAlertEventById(intVar)
	if err != nil {
		sc.Logger.Error("根据id找告警事件错误", zap.Any("告警事件", id), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	//  做个保护，查询一下template 中有没有用到 这个id的
	dbSendGroups, err := models.GetMonitorSendGroupByPoolId(uint(intVar))
	if len(dbSendGroups) > 0 {
		msg := "根据MonitorAlertEvent-id找到了发送组配置-禁止删除告警事件"
		sc.Logger.Error(msg, zap.Any("告警事件", id), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}

	err = dbObj.DeleteOne()
	if err != nil {
		sc.Logger.Error("根据id删除告警事件错误", zap.Any("告警事件", id), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}
	common.OkWithMessage("删除成功", c)
}

func eventAlertSilenceOne(c *gin.Context, intVar int, timeString string, sdr time.Duration, byNameBool bool) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	// 通过 Fingerprint 去查询到event ，再拼接标签
	event, err := models.GetMonitorAlertEventById(intVar)
	if err != nil {
		common.ReqBadFailWithMessage(fmt.Sprintf("通过 id 去查询到event错误:%v", err.Error()), c)
		return
	}
	event.FillFrontAllData()
	if event.SendGroup == nil {
		common.ReqBadFailWithMessage("event的sendgroup为空无法 获取ALertmanager", c)
		return
	}

	// 找到 alm 的地址
	alm, err := models.GetMonitorAlertmanagerPoolById(int(event.SendGroup.PoolId))
	if err != nil {
		common.ReqBadFailWithMessage(fmt.Sprintf("通过 event.SendGroup.PoolId 去查询到alm错误:%v", err.Error()), c)
		return
	}

	if len(alm.AlertmanagerInstances) == 0 {
		common.ReqBadFailWithMessage(fmt.Sprintf("alm.AlertmanagerInstances长度为0 :%v", err.Error()), c)
		return
	}

	// 找到这个人

	userName := c.MustGet(common.GIN_CTX_JWT_USER_NAME).(string)
	dbUser, err := models.GetUserByUserName(userName)
	if err != nil {
		sc.Logger.Error("通过token解析到的userName去数据库中找User失败",
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("通过token解析到的userName去数据库中找User失败:%v", err.Error()), c)
		return
	}

	matchers := labels.Matchers{}
	event.GenMapFromKvs()
	for k, v := range event.LabelsM {
		m := &labels.Matcher{
			Type:  labels.MatchEqual,
			Name:  k,
			Value: v,
		}
		matchers = append(matchers, m)
	}
	if byNameBool {
		matchers = labels.Matchers{
			{
				Type:  labels.MatchEqual,
				Name:  common.MONITOR_ALERT_NAME_KEY,
				Value: event.LabelsM[common.MONITOR_ALERT_NAME_KEY],
			},
		}
	}

	si := types.Silence{
		Matchers:  matchers,
		StartsAt:  time.Now(),
		EndsAt:    time.Now().Add(time.Duration(sdr)),
		CreatedBy: dbUser.RealName,
		Comment:   fmt.Sprintf("由前端页面通过event过来的屏蔽:eventId :%v 操作人:%v 屏蔽时间:%v 是否byName:%v", event.ID, dbUser.RealName, sdr, byNameBool),
		Status:    types.SilenceStatus{},
	}

	jsonStr, _ := json.Marshal(si)
	//
	almAddr := fmt.Sprintf("http://%v:9093", alm.AlertmanagerInstances[0])
	url := fmt.Sprintf("%s/%s", almAddr, "api/v1/silences")
	emptyMap := map[string]string{}
	bodyBytes, err := common.PostWithJsonString(
		sc.Logger, "AlertSilence",
		sc.HttpRequestGlobalTimeoutSeconds,
		url,
		string(jsonStr),
		emptyMap,
		emptyMap,
	)

	if err != nil {
		sc.Logger.Error("告警静默调用alertmanager接口失败",
			zap.Error(err),
			zap.Any("fingerprint", event.Fingerprint),
			zap.Any("id", intVar),
			zap.Any("out", string(bodyBytes)),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("告警静默调用alertmanager接口失败:%v", err.Error()), c)
		return
	}

	// 解析 屏蔽结果
	var sr *common.AlertmanagerSilenceResponse
	err = json.Unmarshal(bodyBytes, &sr)
	if err != nil {
		sc.Logger.Error("告警静默解析 屏蔽结果",
			zap.Error(err),
			zap.Any("fingerprint", event.Fingerprint),
			zap.Any("id", intVar),
			zap.Any("out", string(bodyBytes)),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("告警静默解析 屏蔽结果失败:%v", err.Error()), c)
		return
	}
	if sr != nil {
		event.SilenceId = sr.Data.SilenceId
		event.Status = common.MONITOR_ALERT_STATUS_SILENCED
		event.UpdateOne()
	}

	// 屏蔽成功后 异步发送飞书消息
	msg := fmt.Sprintf("时间:%v 用户:%v 动作:%v 告警:%v",
		common.TimeNowString(),
		dbUser.RealName,
		fmt.Sprintf("屏蔽告警:%v", timeString),
		event.AlertName,
	)
	go event.SendImMessageToQunByEvent(msg, sc.ImFeishuC.QunLiaoMessageApi, sc.Logger, sc.ImFeishuC.RequestTimeoutSeconds)

	//common.OkWithMessage("屏蔽成功", c)

}

func eventAlertSilence(c *gin.Context) {

	// event 写入库的时候 把标签全部写入，silence
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	var reqObj EventSilenceRequest
	err := c.ShouldBindJSON(&reqObj)
	if err != nil {
		sc.Logger.Error("解析event屏蔽请求错误", zap.Any("告警事件", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 在这里校验字段，是否必填，范围是否正确
	err = validate.Struct(reqObj)
	if err != nil {

		// 这里为什么要判断错误是否是 ValidationErrors
		if errors, ok := err.(validator.ValidationErrors); ok {
			common.ReqBadFailWithWithDetailed(

				gin.H{
					"翻译前": err.Error(),
					"翻译后": errors.Translate(trans),
				},
				"请求出错",
				c,
			)
			return
		}
		common.ReqBadFailWithMessage(err.Error(), c)
		return

	}

	id := c.Param("id")
	intVar, _ := strconv.Atoi(id)

	byNameBool := reqObj.ByName

	sdr, err := model.ParseDuration(reqObj.SilenceTime)
	if err != nil {
		msg := fmt.Sprintf("解析屏蔽时间错误：%v", err)
		sc.Logger.Error(msg, zap.Any("告警事件", reqObj), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}

	eventAlertSilenceOne(c, intVar, reqObj.SilenceTime, time.Duration(sdr), byNameBool)
	common.OkWithMessage("屏蔽成功", c)

}

type batchEventAlertSilenceRequest struct {
	Ids []int `json:"ids"`
	*EventSilenceRequest
}

func batchEventAlertSilence(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	var reqObj batchEventAlertSilenceRequest
	err := c.ShouldBindJSON(&reqObj)
	if err != nil {
		sc.Logger.Error("解析批量屏蔽告警配置请求失败", zap.Any("告警规则配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 在这里校验字段，是否必填，范围是否正确
	err = validate.Struct(reqObj)
	if err != nil {

		// 这里为什么要判断错误是否是 ValidationErrors
		if errors, ok := err.(validator.ValidationErrors); ok {
			common.ReqBadFailWithWithDetailed(

				gin.H{
					"翻译前": err.Error(),
					"翻译后": errors.Translate(trans),
				},
				"请求出错",
				c,
			)
			return
		}
		common.ReqBadFailWithMessage(err.Error(), c)
		return

	}

	byNameBool := reqObj.ByName

	sdr, err := model.ParseDuration(reqObj.SilenceTime)
	if err != nil {
		msg := fmt.Sprintf("解析屏蔽时间错误：%v", err)
		sc.Logger.Error(msg, zap.Any("告警事件", reqObj), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}
	for _, id := range reqObj.Ids {
		id := id
		eventAlertSilenceOne(c, id, reqObj.SilenceTime, time.Duration(sdr), byNameBool)

	}

	common.OkWithMessage("批量屏蔽成功", c)
}

func eventAlertRenLing(c *gin.Context) {

	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	id := c.Param("id")
	intVar, _ := strconv.Atoi(id)

	// 通过 Fingerprint 去查询到event ，再拼接标签
	event, err := models.GetMonitorAlertEventById(intVar)
	if err != nil {
		common.ReqBadFailWithMessage(fmt.Sprintf("通过 id 去查询到event错误:%v", err.Error()), c)
		return
	}
	event.FillFrontAllData()

	// 找到这个人

	userName := c.MustGet(common.GIN_CTX_JWT_USER_NAME).(string)
	dbUser, err := models.GetUserByUserName(userName)
	if err != nil {
		sc.Logger.Error("通过token解析到的userName去数据库中找User失败",
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("通过token解析到的userName去数据库中找User失败:%v", err.Error()), c)
		return
	}

	event.RenLingUserId = dbUser.ID
	event.Status = common.MONITOR_ALERT_STATUS_RENLING
	err = event.UpdateOne()
	if err != nil {
		sc.Logger.Error("更新event错误",
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("更新event错误:%v", err.Error()), c)
		return
	}
	// 屏蔽成功后 异步发送飞书消息
	msg := fmt.Sprintf("时间:%v 用户:%v 动作:%v 告警:%v",
		common.TimeNowString(),
		dbUser.RealName,
		"认领告警",
		event.AlertName,
	)
	go event.SendImMessageToQunByEvent(msg, sc.ImFeishuC.QunLiaoMessageApi, sc.Logger, sc.ImFeishuC.RequestTimeoutSeconds)

	common.OkWithMessage("认领成功", c)

}

func eventAlertUnSilence(c *gin.Context) {

	// event 写入库的时候 把标签全部写入，silence
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	id := c.Param("id")
	intVar, _ := strconv.Atoi(id)

	// 通过 Fingerprint 去查询到event ，再拼接标签
	event, err := models.GetMonitorAlertEventById(intVar)
	if err != nil {
		common.ReqBadFailWithMessage(fmt.Sprintf("通过 id 去查询到event错误:%v", err.Error()), c)
		return
	}
	event.FillFrontAllData()
	if event.SendGroup == nil {
		common.ReqBadFailWithMessage("event的sendgroup为空无法 获取ALertmanager", c)
		return
	}

	if event.SilenceId == "" {
		common.ReqBadFailWithMessage("SilenceId为空请联系管理员", c)
		return
	}

	// 找到这个人

	userName := c.MustGet(common.GIN_CTX_JWT_USER_NAME).(string)
	dbUser, err := models.GetUserByUserName(userName)
	if err != nil {
		sc.Logger.Error("通过token解析到的userName去数据库中找User失败",
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("通过token解析到的userName去数据库中找User失败:%v", err.Error()), c)
		return
	}

	url := fmt.Sprintf("%s/%s/%v", sc.AlertManagerApi, "api/v1/silence", event.SilenceId)
	emptyMap := map[string]string{}
	bodyBytes, err := common.DeleteWithId(
		sc.Logger, "AlertSilence",
		sc.HttpRequestGlobalTimeoutSeconds,
		url,
		emptyMap,
		emptyMap,
	)

	if err != nil {
		sc.Logger.Error("告警解除屏蔽调用alertmanager接口失败",
			zap.Error(err),
			zap.Any("fingerprint", event.Fingerprint),
			zap.Any("id", id),
			zap.Any("out", string(bodyBytes)),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("告警静默调用alertmanager接口失败:%v", err.Error()), c)
		return
	}

	event.Status = common.MONITOR_ALERT_STATUS_FIRING
	event.UpdateOne()
	// 屏蔽成功后 异步发送飞书消息
	msg := fmt.Sprintf("时间:%v 用户:%v 动作:%v 告警:%v",
		common.TimeNowString(),
		dbUser.RealName,
		"解除屏蔽告警",
		event.AlertName,
	)
	go event.SendImMessageToQunByEvent(msg, sc.ImFeishuC.QunLiaoMessageApi, sc.Logger, sc.ImFeishuC.RequestTimeoutSeconds)

	common.OkWithMessage("屏蔽成功", c)

}
